from tsy.tsy_way import Greedy
from kit.verifier import Verifier
from kit.data import Data
import random
import copy

random.seed(0)


class GeneticSolver:
    def __init__(self):
        self.epoch = 200
        self.group_num = 1000
        self.n = 40
        self.relax_coe = 2
        self.mutation_line = 0.6

    def init_group(self):
        order = [20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
                 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
                 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
                 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
        group = list()
        group.append([self.calc(order), order])
        while len(group) < self.group_num:
            new_order = copy.deepcopy(order)
            random.shuffle(new_order)
            group.append([self.calc(new_order), new_order])
        return group

    def calc(self, order):
        solver = Greedy(order=order)
        val = solver.solve()
        return val

    def mutation(self, pso):
        x = random.randint(0, self.n - 1)
        y = random.randint(0, self.n - 1)
        if x > y:
            x, y = y, x
        new_pso = copy.deepcopy(pso)
        for i in range(x, (x + y) // 2 + 1):
            new_pso[i], new_pso[x + y - i] = new_pso[x + y - i], new_pso[i]
        return new_pso

    def crossover(self, pso1, pso2):
        new_pso1 = list()
        new_pso2 = list()
        st = set()
        for i in range(self.n // 2):
            st.add(pso1[i])
            new_pso1.append(pso1[i])
        for i in range(self.n):
            if pso2[i] not in st:
                new_pso1.append(pso2[i])
            if pso2[i] in st:
                new_pso2.append(pso2[i])
        for i in range(self.n // 2, self.n):
            new_pso2.append(pso1[i])
        assert len(new_pso1) == self.n and len(new_pso2) == self.n
        return new_pso1, new_pso2

    def solve(self):
        group = self.init_group()
        history_best = 100000
        best_order = None
        for i in range(self.epoch):
            epoch_best = 100000
            epoch_avg = 0
            for j in range(self.group_num):
                val = group[j][0]
                epoch_avg += val
                epoch_best = min(epoch_best, val)
                if val < history_best:
                    history_best = val
                    best_order = group[j][1]
            print('epoch %d: epoch_avg-%.2f epoch_best-%.2f history_best-%.2f' % (
            i + 1, epoch_avg / self.group_num, epoch_best, history_best))
            new_group = list()
            while len(new_group) < self.relax_coe * self.group_num:
                p = random.random()
                if p < self.mutation_line:
                    pso = self.mutation(group[random.randint(0, len(group) - 1)][1])
                    new_group.append([self.calc(pso), pso])
                else:
                    pso1, pso2 = self.crossover(group[random.randint(0, len(group) - 1)][1],
                                                group[random.randint(0, len(group) - 1)][1])
                    new_group.append([self.calc(pso1), pso1])
                    new_group.append([self.calc(pso2), pso2])
            new_group.sort(key=lambda x: x[0])
            group = new_group[:self.group_num]
        return best_order


if __name__ == '__main__':
    # data = Data()
    # data.show_data()
    file_path = 'data/tsy_sol.csv'
    solver = GeneticSolver()
    best_order = solver.solve()
    print(best_order)
    solver = Greedy(order=best_order, output_path=file_path)
    solver.solve()
    solver.write_ans()
    verifier = Verifier()
    verifier.verify(sol_path=file_path)
    verifier.score(sol_path=file_path)